--[[--
类名: StringEx.lua
创建日期: 2015-1-20
创建人: 陈小虎
功能描述: string扩展
修改内容：
修改日期:   
修改人:     
修改备注: 
]]


local table_insert = table.insert
local table_concat = table.concat

local utf8 = require 'lua-utf8.c'
local utf8_len = utf8.len
local utf8_sub = utf8.sub

local string_find = string.find
local string_sub = string.sub
local string_len = string.len
local string_byte = string.byte
local string_char = string.char
local string_gsub = string.gsub
local string_format = string.format
local string_match = string.match
local string_split = string.split

--返回UTF-8字符数组
string.utable=function(str)
    local tab={}
    local len = utf8_len(str)
    for i=1,len do
        table_insert(tab,utf8.char(utf8.byte(str,i)))
    end
    return tab
end

--utf-8字符个数
string.ulen = function(str)
    return utf8_len(str)
end

--一个非ASCII字符算2个
string.counting = function(str)
    local len = 0
    for pos, code in utf8.next, str do
        if code > 127 then
            len = len + 2
        else
            len = len + 1
        end
    end
    return len
end

string.usub = utf8.sub

string.usubstr = function(str,sum)
    return utf8_sub(str,1,sum)
end

--omit string like "xxxxx..." when str length greater then len
string.uomitstr = function(str,limit)
    local len = utf8_len(str)
    if len <= limit then
        return str
    else
        return utf8_sub(str,1,limit).."..."
    end 
end

string.parseUrl = function(str)
    local protocol = ""
    local hosts = ""
    local port = ""
    local path = "/"

    local pos = string_find(str,"://")
    if pos then
        protocol = string_sub(str,1,pos-1)
        str = string_sub(str,pos+3)
    end

    local pos1 = string_find(str,"/",0)
    local pos2 = string_find(str,":",0)
    if pos1 then
        path = path..string_sub(str,pos1+1,string_len(str))

        if pos2 then
            port = string_sub(str, pos2+1, pos1-1)
            hosts = string_sub(str, 0, pos2-1)
        else
            hosts = string_sub(str, 0, pos1-1)
        end
    else
        if pos2 then
            port = string_sub(str, pos2+1)
            hosts = string_sub(str, 0, pos2-1)
        else
            hosts = str
        end
    end

    return protocol,hosts,port,path
end

string.matchIPV4 = function(str)
    return string_match(str,"^%d+%.%d+%.%d+%.%d+$")~=nil
end

-- 对string进行两层分割
function string.splitTwice(str, deliAry, isNum)
    deliAry = deliAry or {";", "|"}
    isNum = isNum or false
    local arys = string_split(str, deliAry[1])
    for i, ary in ipairs(arys) do
        arys[i] = string_split(ary, deliAry[2])
        if isNum then
            for j, v in ipairs(arys[i]) do
                arys[i][j] = tonumber(v)
            end
        end
    end
    return arys
end

-- 对string进行三层分割
function string.splitThrice(str, deliAry, isNum)
    deliAry = deliAry or {";", ":", "|"}
    isNum = isNum or false
    local deliTwiceAry = {deliAry[2], deliAry[3]}
    local arys = string_split(str, deliAry[1])
    for i, ary in ipairs(arys) do
        arys[i] = string.splitTwice(ary, deliTwiceAry, isNum)
    end
    return arys
end

--lua 转义字符串中的特殊字符
function string.luaSpecialChars(str, reg)
    reg = reg or "([%(%)%.%%%+%-%*%?%^%$%[%]])"
    return string_gsub(str, reg, "%%%1")
end

--xml 转义字符串中的特殊字符
function string.xmlSpecialChars(str, regs)
    regs = regs or {"<"}
    for i,v in ipairs(regs) do
        str = string_gsub(str, v, string._htmlspecialchars_set[v])
    end
    return str
end

-- 去除输入字符串的空白字符，返回结果
function string.trimAll(input)
    return string_gsub(input, "[ \t\n\r]+", "")
end

-- 从后往前搜索
function string.reverseFind(input, pattern)
    local pos, start, en = 0, nil, nil
    for st,sp in function() return string.find(input, pattern, pos, true) end do
        start = st
        en = sp
        pos = sp + 1
    end
    return start, en
end

function string.uuid()
    local random = math.random
    local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
    return string_gsub(template, '[xy]', function (c)
        local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
        return string_format('%x', v)
    end)
end

--字符串是否是以特定字面开始
function string.isStartWith(str, startStr)
    return string_find(str, "^" .. startStr) ~= nil
end
--字符串是否是以特定字面结束
function string.isEndWith(str, endStr)
    return string_find(str, endStr .. "$") ~= nil
end
--字符串是否包含了特定字符串
function string.isContain(str, tarStr)
    local sIndex, endIndex = string_find(str, tarStr)
    return sIndex ~= nil
end

--字符是否是字母和数字
function string.isNumberOrChar(str)
    local temp = string_byte(str)
    if 48 <= temp and temp <= 57 then                                 
        return true        
    elseif 65<= temp and temp <= 90 then 
        return true    
    elseif 97 <= temp and temp <= 122 then 
        return true     
    else 
        return false
    end
end
--计算str的总长度 中文算1个单位，英文算0.5个单位长度
function string.countStrLen(str, fontSize)
    local fontSize = fontSize or 1
    local lenInByte = #str
    local width = 0
    local i = 1
    while true do
        if i <= lenInByte then
            local curByte = string_byte(str, i)
            if curByte>0 and curByte<=127 then
                byteCount = 1
            elseif curByte>=192 and curByte<223 then
                byteCount = 2
            elseif curByte>=224 and curByte<239 then
                byteCount = 3
            elseif curByte>=240 and curByte<=247 then
                byteCount = 4
            end
            if byteCount == 1 then
                width = width + fontSize * 0.5
            else
                width = width + fontSize
            end
            -- local char = string_sub(str, i, i+byteCount-1)
            i = i + byteCount
        else
            break
        end
    end
    return width
end

--移除字符串中的中文 return新str，和移除的中文个数
function string.removeChinese(str)
    local lenInByte = #str
    local ret = ""
    local removeCnt = 0
    local i = 1
    while true do
        if i <= lenInByte then
            local curByte = string_byte(str, i)
            if curByte>0 and curByte<=127 then
                byteCount = 1
            elseif curByte>=192 and curByte<223 then
                byteCount = 2
            elseif curByte>=224 and curByte<239 then
                byteCount = 3
            elseif curByte>=240 and curByte<=247 then
                byteCount = 4
            end
            local char = string_sub(str, i, i+byteCount-1)
            i = i + byteCount
            if byteCount == 1 then
                ret = ret .. char
            else
                removeCnt = removeCnt + 1
            end
        else
            break
        end
    end
    return ret, removeCnt
end

---通过文件的 "..." 获取文件名字
function string.getModeName(str)
    if ".lua" == string_sub(str, -4) then
        str = string_sub(str, 1, -5)
    end
    local ary 
    if string_find(str, "/") then
        ary = string_split(str, "/")
    else
        ary = string_split(str, ".")
    end
    return ary[#ary]
end


--过滤特殊字符，保留中文、英文和数字。
function string.filter_spec_chars(s)  
    local ss = {}
    for k = 1, #s do
        local c = string_byte(s,k)
        if not c then break end
        if c<192 then
            if (c>=48 and c<=57) or (c>= 65 and c<=90) or (c>=97 and c<=122) then
                table_insert(ss, string_char(c))
            end
        elseif c<224 then
            k = k + 1
        elseif c<240 then
            if c>=228 and c<=233 then
                local c1 = string_byte(s,k+1)
                local c2 = string_byte(s,k+2)
                if c1 and c2 then
                    local a1,a2,a3,a4 = 128,191,128,191
                    if c == 228 then a1 = 184
                    elseif c == 233 then a2,a4 = 190,c1 ~= 190 and 191 or 165
                    end
                    if c1>=a1 and c1<=a2 and c2>=a3 and c2<=a4 then
                        table_insert(ss, string_char(c,c1,c2))
                    end
                end
            end
            k = k + 2
        elseif c<248 then
            k = k + 3
        elseif c<252 then
            k = k + 4
        elseif c<254 then
            k = k + 5
        end
    end
    return table_concat(ss)
end